package be.ipl.finito.domaine;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapKey;
import javax.persistence.MapKeyJoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;

import be.ipl.finito.util.CaseGrille;
import be.ipl.finito.util.RandomUtil;
import be.ipl.finito.util.Util;

@SuppressWarnings("serial")
@Entity
@Table(name = "PARTIES", schema = "FINITO")
public class Partie implements Serializable {
	public static final int TEMPS_INACTIVITE = 30;
	public static final int MAX_JOUEURS = 3;
	public static final int MAX_JETON_TOUR1 = 3;
	public static final int TEMPS_DEBUT_PARTIE = 15;
	public static final int MIN_JOUEURS = 2;
	public static final int NBR_JETONS = 12;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int id;

	@NotNull
	@Column(unique = true)
	private String nom;

	@NotNull
	@Column
	private int tour = 0;

	@NotNull
	@Enumerated(EnumType.STRING)
	private Etat etat = Etat.EN_ATTENTE;

	@OneToMany(fetch = FetchType.EAGER)
	@JoinColumn(name = "PARTIE_ID")
	@MapKey(name = "id")
	private Map<Integer, Grille> grilles = new HashMap<Integer, Grille>();

	// garde chaque jeton et sa positon_tirage
	@ElementCollection
	@CollectionTable(schema = "FINITO", name = "JETONS_JOUES")
	@Column(name = "POSITION_TIRAGE")
	@MapKeyJoinColumn(name = "ID_JETON")
	private Map<Jeton, Integer> jetonsJoues = new HashMap<Jeton, Integer>(
			NBR_JETONS);

	@Transient
	private List<Jeton> jetonsJouables = new ArrayList<Jeton>(NBR_JETONS);
	@Transient
	private Map<Grille, Integer> pointsGrilles = new HashMap<Grille, Integer>();

	
	//public static final int NBR_CASES = 20;

	@Transient
	private final RandomUtil random = new RandomUtil();

	protected Partie() {

	}

	public Partie(String nom) {
		Util.checkString(nom);
		this.nom = nom;

		for (int i = 0; i < NBR_JETONS; i++) {
			this.jetonsJouables.add(new Jeton(i + 1));
		}
		Collections.shuffle(this.jetonsJouables);
	}

	public enum Etat {
		EN_ATTENTE() {
			// Puls de @Override dans cet enum svp
			public boolean inscrireJoueur(Grille grilleJoueur,
					final Partie partie) {
				Util.checkObject(grilleJoueur);
				Util.checkObject(partie);

				if (grilleJoueur.getProprietaire() == null) {
					return false;
				}

				if (partie.grilles.containsValue(grilleJoueur)) {
					return false;
				}
				partie.grilles.put(grilleJoueur.getId(), grilleJoueur);
				return true;
			}

			// Lancement de la partie etat=Etat.EN_COURS
			public boolean lancerPartie(Partie partie) {
				Util.checkObject(partie);
				if (partie.grilles.size() < 2) {
					return false;
				}
				partie.etat = Etat.EN_COURS;
				
				return true;
			}

		},
		EN_COURS() {
			// premier tirage
			public List<Jeton> premierTirage(Partie partie) {
				Util.checkObject(partie);
				if (partie.jetonsJouables.size() < 1) {
					return null;
				}

				List<Jeton> troisJentons = new ArrayList<Jeton>();

				for (int i = 0; i < MAX_JETON_TOUR1; i++) {
					troisJentons.add(this.tirerJeton(partie));
				}
				return troisJentons;
			}

			// Autres tirages

			public Jeton autreTirage(Partie partie) {
				Util.checkObject(partie);
				if (partie.tour < 3) {
					return null;
				}

				return this.tirerJeton(partie);
			}

			// tire un jeton à un tour
			Jeton tirerJeton(Partie partie) {
				Util.checkObject(partie);
				Jeton j = partie.jetonsJouables.get(partie.tour);
				partie.tour++;
				return j;
			}

			boolean ajouterUnJetonJoue(Jeton jeton, Partie partie,int position){
				Util.checkObject(jeton);
				Util.checkObject(partie);
				Util.checkPositive(position);
				partie.jetonsJoues.put(jeton, position);
				return true;				
			}
			
			public boolean suspendrePartie(Partie partie) {
				// TODO
				return false;

			}

			// reprendre la partie
			public boolean reprendrePartie(Partie partie) {
				// TODO
				return false;

			}

			// termimer partie etat=Etat.FINIE
			public boolean terminePartie(Partie partie) {
				Util.checkObject(partie);
				if (!this.verifierFinPartie(partie)) {
					return false;
				}
				partie.etat = Etat.FINIE;
				return true;
			}

			// vérifie si la partie est terminée
			public boolean verifierFinPartie(Partie partie) {
				Util.checkObject(partie);
				List<Grille> gs = new ArrayList<Grille>(partie.grilles.values());

				if (gs.size() < 2) {
					return false;
				}
				for (Grille g : gs) {
					if (g.getCaseOccupees().size() < 12) {
						return false;
					}
				}
				return true;
			}
		},
		FINIE() {
			//
			// Calculer les points

			public void caluculerPoints(Partie partie, Grille grille) {
				Util.checkObject(partie);
				Util.checkObject(grille);

				List<Grille> gs = new ArrayList<Grille>(partie.grilles.values());
				if (!gs.contains(grille)) {
					return;
				}
				// List<CaseGrille>cg=grille.getCaseOccupees();
				int max = -1;
				int suite = 0;
				int point = 0;
				for (CaseGrille cg : grille.getCaseOccupees()) {
					int numJeton = cg.getJeton().getNumero();
					if (numJeton > max) {
						suite++;
						max = numJeton;
					} else {
						point += suite;
						suite = 0;
						max = numJeton;
					}
				}
				partie.pointsGrilles.put(grille, point);

			}

			// Déterminer vainqueur(s)
			public List<Joueur> determinerVaiqueur(Partie partie) {
				Util.checkObject(partie);
				List<Grille> g = new ArrayList<Grille>(
						partie.pointsGrilles.keySet());

				List<Joueur> vainqueur = new ArrayList<Joueur>();

				for (Grille grille : g) {

					if (partie.pointsGrilles.get(grille) > Partie.NBR_JETONS) {

						vainqueur.add(grille.getProprietaire());
					}
				}
				return vainqueur;
			}
			// afficher le tableaux (+score +vainqueur) de chaque joueur à tous
			// les
			// autres
		};
		boolean ajouterUnJetonJoue(Jeton jeton, Partie partie,int position){
			
			return false;
		}
		public boolean lancerPartie(Partie partie) {
			return false;
		}

		public boolean inscrireJoueur(Grille grilleJoueur, final Partie partie) {
			return false;
		}

		public List<Jeton> premierTirage(Partie partie) {
			return null;
		}

		public Jeton autreTirage(Partie partie) {
			return null;
		}

		public boolean suspendrePartie(Partie partie) {
			return false;
		}

		public boolean reprendrePartie(Partie partie) {
			return false;
		}

		public boolean verifierFinPartie(Partie partie) {

			return false;
		}

		public boolean terminePartie(Partie partie) {
			return false;
		}

		public void caluculerPoints(Partie partie, Grille grille) {

		}

		public List<Joueur> determinerVaiqueur(Partie partie) {
			return null;
		}
	}
	
	

	public int getId() {
		return this.id;
	}

	public String getNom() {
		return this.nom;
	}

	public Etat getEtat() {
		return this.etat;
	}

	public List<Grille> getGrillesDesJoueurs() {
		return new ArrayList<Grille>(this.grilles.values());
	}

	public List<Jeton> getJetonsJouable() {
		return new ArrayList<Jeton>(this.jetonsJouables);
	}

	public boolean lancerPartie() {
		return this.etat.lancerPartie(this);
	}

	boolean suspendrePartie() {
		return this.etat.suspendrePartie(this);
	}

	boolean reprendrePartie() {
		return this.etat.reprendrePartie(this);
	}

	public List<Jeton> premierTirage() {
		return this.etat.premierTirage(this);
	}

	public Jeton autreTirage() {
		return this.etat.autreTirage(this);
	}
	
	public int getTour() {
		return this.tour;
	}
	
	public void tourSuivant(){
		this.tour ++ ;
	}
	
	
	public boolean ajouterUnJetonJoue(Jeton jeton, int position){
		
		return etat.ajouterUnJetonJoue(jeton, this, position);
	}

	public boolean verifierFinPartie() {
		return this.etat.verifierFinPartie(this);
	}

	public boolean terminePartie() {
		return this.etat.terminePartie(this);
	}

	public void caluculerPoints(Grille grille) {
		this.etat.caluculerPoints(this, grille);
	}

	public List<Joueur> determinerVaiqueur() {
		return this.etat.determinerVaiqueur(this);
	}

	public boolean inscrireJoueur(Grille grilleJoueur) {
		return this.etat.inscrireJoueur(grilleJoueur, this);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + this.id;
		return result;
		
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (this.getClass() != obj.getClass()) {
			return false;
		}
		Partie other = (Partie) obj;
		if (this.id != other.id) {
			return false;
		}
		return true;
	}

	@Transient
	public RandomUtil getRandom() {
		return this.random;
	}

	public int getNombreJoueurs() {
		return this.grilles.size();
	}

}